package org.hydrakyoufeng.stuplatform.service.impl;

import jakarta.servlet.http.HttpServletRequest;
import net.bytebuddy.utility.RandomString;
import org.hydrakyoufeng.stuplatform.dto.AdminLoginDTO;
import org.hydrakyoufeng.stuplatform.dto.UserDTO;
import org.hydrakyoufeng.stuplatform.dto.UserLoginDTO;
import org.hydrakyoufeng.stuplatform.exception.ProcessException;
import org.hydrakyoufeng.stuplatform.mapper.AdminMapper;
import org.hydrakyoufeng.stuplatform.mapper.UserMapper;
import org.hydrakyoufeng.stuplatform.service.LoginService;
import org.hydrakyoufeng.stuplatform.utils.*;
import org.hydrakyoufeng.stuplatform.vo.AdminLoginVO;
import org.hydrakyoufeng.stuplatform.vo.PermissionListVO;
import org.hydrakyoufeng.stuplatform.vo.UserLoginVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class LoginServiceImpl implements LoginService {

    private final AdminMapper adminMapper;

    private final UserMapper userMapper;

    private final RedisTemplate redisTemplate;

    private final JWTUtils jwtUtils;

    private final VerificationCodeUtils verificationCodeUtils;

    /**
     * md5编码工具类
     */
    private final Md5Utils md5Utils;

    /**
     * 短信工具类
     */
    private final MsgCodeUtils msgCodeUtils;

    @Autowired
    public LoginServiceImpl(AdminMapper adminMapper,
                            UserMapper userMapper,
                            RedisTemplate redisTemplate,
                            JWTUtils jwtUtils,
                            VerificationCodeUtils verificationCodeUtils,
                            Md5Utils md5Utils,
                            MsgCodeUtils msgCodeUtils) {
        this.adminMapper = adminMapper;
        this.userMapper = userMapper;
        this.redisTemplate = redisTemplate;
        this.jwtUtils = jwtUtils;
        this.verificationCodeUtils = verificationCodeUtils;
        this.md5Utils = md5Utils;
        this.msgCodeUtils = msgCodeUtils;
    }

    public ResultEntity adminLogin(AdminLoginDTO adminLoginDto,HttpServletRequest request) throws Exception {
        verificationCodeUtils.valideteCode(adminLoginDto.getUuid(),
                adminLoginDto.getVerificationCode());
        AdminLoginVO adminLoginVO = adminMapper.login(adminLoginDto);
        if (null == adminLoginVO) {
            throw new ProcessException("电话号码或密码错误");
        }
        List<PermissionListVO> AdminModelVO = adminMapper.queryPermissionList(adminLoginVO.getId());
        Set<String> permissionSet = new TreeSet<>();
        // 将权限列表中的uri添加到集合中
        AdminModelVO.forEach(v -> {
            v.getPermissionList().forEach(v2 -> {
                // 一个权限中可以支持多个uri，多个uri使用,隔开。
                String[] uris = v2.getUri().split(",");
                Arrays.stream(uris).forEach(uri -> {
                    permissionSet.add(uri);
                });
            });
        });
        // 将权限列表和uri集合放到结果对象中
        adminLoginVO.setPermissionList(AdminModelVO);
        adminLoginVO.setPermissionSet(permissionSet);

        // 生成tokenMap
        Map<String,Object> tokenMap = jwtUtils.createTokenMap(adminLoginVO.getId(),"ADM");
        String tokenStr = jwtUtils.encodeTokenMap(tokenMap);
        String cacheKey = jwtUtils.generateCacheKey(tokenMap,jwtUtils.getIp(request));

        // token中也需要保存权限列表
        tokenMap.put("permissionSet",permissionSet);
        // 调用redisTemplate，将token的信息保存到缓存中
        redisTemplate.opsForValue().set(cacheKey,tokenMap);

        // 将token返回给前端
        adminLoginVO.setToken(tokenStr);

        return ResultEntity.success("登录成功",adminLoginVO);
    }

    @Override
    public ResultEntity userLogon(UserDTO userDTO) throws Exception {
        // 生成6为随机数作为盐
        String salt = RandomString.make(6);
        userDTO.setSalt(salt);
        // 将就之前用户增加的逻辑
        if (userMapper.insert(userDTO) == 1) {
            return ResultEntity.success("注册成功");
        } else {
            return ResultEntity.success("注册失败");
        }
    }

    @Override
    public ResultEntity queryUserCount(String mobile) {
        UserDTO userDTO = new UserDTO();
        userDTO.setMobile(mobile);
        return ResultEntity.success("查询成功",userMapper.queryCount(userDTO));
    }

    /**
     * 获取登录验证码，以base64的加密字符串方式返回图片
     */
    public ResultEntity verificationCode() throws Exception {
        return ResultEntity.success("获取成功",verificationCodeUtils.getAndSave());
    }

    /**
     * 用户使用手机号码和密码登录
     * @param userLoginDTO
     * @param request
     * @return
     * @throws Exception
     */
    @Override
    public ResultEntity userLoginByPassword(UserLoginDTO userLoginDTO,
                                            HttpServletRequest request)
            throws Exception {
        verificationCodeUtils.valideteCode(userLoginDTO.getUuid(),
                userLoginDTO.getVerificationCode());
        UserLoginVO userLoginVO = userMapper.login(userLoginDTO);
        if (null == userLoginVO) {
            throw new ProcessException("电话号码或密码错误");
        }
        if (null == userLoginVO.getPassword() || "".equals(userLoginVO.getPassword())) {
            throw new ProcessException("用户未设置密码，请使用验证码登录");
        }
        if (!"0".equals(userLoginVO.getStatus())) {
            throw new ProcessException("该账号目前无法使用");
        }

        // 判断：md5(用户输入密码+盐）是否等于数据库中的密码
        String passwordInput = md5Utils.encode(userLoginDTO.getPassword() + userLoginVO.getSalt());
        if (!userLoginVO.getPassword().equals(passwordInput)) {
            throw new ProcessException("电话号码或密码错误");
        }

        // 生成tokenMap
        Map<String,Object> tokenMap = jwtUtils.createTokenMap(userLoginVO.getId(),"USR");
        String tokenStr = jwtUtils.encodeTokenMap(tokenMap);
        String cacheKey = jwtUtils.generateCacheKey(tokenMap,jwtUtils.getIp(request));

        // 调用redisTemplate，将token的信息保存到缓存中
        redisTemplate.opsForValue().set(cacheKey,tokenMap);

        // 将token返回给前端
        userLoginVO.setToken(tokenStr);

        // 清空不需要返回到前端的密码和盐的信息
        userLoginVO.setPassword(null);
        userLoginVO.setSalt(null);

        return ResultEntity.success("登录成功",userLoginVO);
    }

    @Override
    public ResultEntity sendMsgCode(UserLoginDTO userLoginDTO) throws Exception {
        verificationCodeUtils.valideteCode(userLoginDTO.getUuid(),
                userLoginDTO.getVerificationCode());
        String code = msgCodeUtils.getAndSendCode(userLoginDTO.getMobile());
        // 注意：测试阶段将验证码通过接口返回给前端，正式上线后不要返回给前端。
        return ResultEntity.success("发送成功",code);
    }

    @Override
    public ResultEntity userLoginByCode(UserLoginDTO userLoginDTO, HttpServletRequest request) throws Exception {
        UserLoginVO userLoginVO = userMapper.login(userLoginDTO);
        // 没有查询到用户信息，说明用户未注册，那么自动对用于进行注册
        if (null == userLoginVO) {
            UserDTO userDTO = new UserDTO();
            userDTO.setMobile(userLoginDTO.getMobile());
            // 生成6为随机数作为盐
            String salt = RandomString.make(6);
            userDTO.setSalt(salt);
            userMapper.insert(userDTO);

            // 补充一些返回参数
            userLoginVO = new UserLoginVO();
            userLoginVO.setId(userDTO.getId());
            userLoginVO.setMobile(userLoginDTO.getMobile());
        } else {
            if (!"0".equals(userLoginVO.getStatus())) {
                throw new ProcessException("该账号目前无法使用");
            }
        }
        msgCodeUtils.validateCode(userLoginDTO.getMobile(),userLoginDTO.getCode());

        // 生成tokenMap
        Map<String,Object> tokenMap = jwtUtils.createTokenMap(userLoginVO.getId(),"USR");
        String tokenStr = jwtUtils.encodeTokenMap(tokenMap);
        String cacheKey = jwtUtils.generateCacheKey(tokenMap,jwtUtils.getIp(request));

        // 调用redisTemplate，将token的信息保存到缓存中
        redisTemplate.opsForValue().set(cacheKey,tokenMap);

        // 将token返回给前端
        userLoginVO.setToken(tokenStr);

        return ResultEntity.success("登录成功",userLoginVO);
    }

    @Override
    public ResultEntity userLogout(HttpServletRequest request) {
        String tokenStr = request.getHeader("Authorization");
        // 如果头信息没有传入token，则直接返回成功
        if (null == tokenStr) {
            return ResultEntity.success("登出成功");
        }
        try {
            Map<String, Object> map = jwtUtils.decodeTokenStr(tokenStr);
            // 生成redis的key
            String key = jwtUtils.generateCacheKey(map, jwtUtils.getIp(request));
            redisTemplate.delete(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 无论如何都告诉前端登出成功
        return ResultEntity.success("登出成功");
    }
}
